#include "QRenderUtility.h"

QRenderUtility::QRenderUtility()
{
    
}

QRenderUtility::~QRenderUtility()
{
    
}

void QRenderUtility::printShaderInfoLog(unsigned int shader)
{
    int logLength = 0;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
    
    if (logLength > 0)
    {
        std::string infoLog(logLength, 0);
        glGetShaderInfoLog(shader, logLength, NULL, &infoLog.at(0));
        std::cerr << infoLog.c_str() << std::endl;
    }
}

void QRenderUtility::printProgramInfoLog(unsigned int program)
{
    int logLength = 0;
    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
    
    if (logLength > 0)
    {
        std::string infoLog(logLength, 0);
        glGetProgramInfoLog(program, logLength, NULL, &infoLog.at(0));
        std::cerr << infoLog.c_str() << std::endl;
    }
}

unsigned char  QRenderUtility::checkError(char *file, int line)
{
    unsigned int error = glGetError();
    if (error != GL_NO_ERROR)
    {
        std::cerr << " > ERROR: " << file << "(" << line << ") ";
        switch (error)
        {
        case GL_INVALID_ENUM:
            std::cerr << "an unacceptable value is specified for an enumerated argument. The offending command is ignored, and has no other side effect than to set the error flag.";
            break;
        case GL_INVALID_VALUE:
            std::cerr << "a numeric argument is out of range. The offending command is ignored, and has no other side effect than to set the error flag.";
            break;
        case GL_INVALID_OPERATION:
            std::cerr << "the specified operation is not allowed in the current state. The offending command is ignored, and has no other side effect than to set the error flag.";
            break;
        case GL_STACK_OVERFLOW:
            std::cerr << "this command would cause a stack overflow. The offending command is ignored, and has no other side effect than to set the error flag.";
            break;
        case GL_STACK_UNDERFLOW:
            std::cerr << "this command would cause a stack underflow. The offending command is ignored, and has no other side effect than to set the error flag.";
            break;
        case GL_OUT_OF_MEMORY:
            std::cerr << "there is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
            break;
        default:
            std::cerr << "unknown.";
            break;
        }
        std::cerr << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

unsigned char QRenderUtility::checkShaderStatus(unsigned int object, unsigned int type)
{
    int status = -1;
    switch (type)
    {
    case GL_COMPILE_STATUS:
        glGetShaderiv(object, type, &status);
        break;
    case GL_LINK_STATUS:
        glGetProgramiv(object, type, &status);
        break;
    }
    if (status == GL_FALSE)
    {
        std::cerr << " > ERROR: checking shader status." << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

unsigned char QRenderUtility::checkBufferStatus(int number)
{
    int maxBuffers = 0;
    glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxBuffers);
    if (maxBuffers < number)
    {
        std::cerr << " > ERROR: checking buffers." << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

unsigned char QRenderUtility::checkFramebufferStatus(unsigned int target)
{
    unsigned int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE)
    {
        std::cerr << " > ERROR: ";
        switch (status)
        {
        case GL_FRAMEBUFFER_UNDEFINED:
            std::cerr << "target is the default framebuffer, but the default framebuffer does not exist.";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
            std::cerr << "any of the framebuffer attachment points are framebuffer incomplete.";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
            std::cerr << "the framebuffer does not have at least one image attached to it.";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
            std::cerr << "the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) named by GL_DRAWBUFFERi.";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
            std::cerr << "GL_READ_BUFFER is not GL_NONE and the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point named by GL_READ_BUFFER.";
            break;
        case GL_FRAMEBUFFER_UNSUPPORTED:
            std::cerr << "the combination of internal formats of the attached images violates an implementation-dependent set of restrictions";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
            std::cerr << "the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES; the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not the same for all attached textures; or, the attached images are a mix of renderbuffers and textures, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached textures.";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
            std::cerr << "any framebuffer attachment is layered, and any populated attachment is not layered, or all populated color attachments are not from textures of the same target.";
            break;
        default:
            std::cerr << "unknown.";
            break;
        }
        std::cerr << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

unsigned char QRenderUtility::checkGlewSupport()
{
    unsigned int error = glewInit();
    if (error != GLEW_OK || !glewIsSupported("GL_VERSION_2_1"))
    {
        std::cerr << " > ERROR: checking glew support." << std::endl;
		std::cerr << " > ERROR: " << glewGetErrorString(error) << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

unsigned char QRenderUtility::checkOpenglSupport()
{
    if (!GL_VERSION_3_0)
    {
        std::cerr << " > ERROR: checking opengl support." << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

unsigned char QRenderUtility::checkShaderSupport()
{
    if (!GL_VERTEX_SHADER || !GL_FRAGMENT_SHADER)
    {
        std::cerr << " > ERROR: checking shader support." << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

unsigned char QRenderUtility::checkArguments(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cerr << " > ERROR: checking arguments." << std::endl;
        std::cerr << " > Usage: jrendering <file.dat>." << std::endl;
        return GL_FALSE;
    }
    return GL_TRUE;
}

void QRenderUtility::trim(std::string &s)
{
    if (!s.empty())
    {
        int found = s.find_first_of('\t');
        while (found != std::string::npos)
        {
            s.replace(found, 1, " ");
            found = s.find_first_of('\t', found + 1);
        }
        s.erase(0, s.find_first_not_of(' '));
        s.erase(s.find_last_not_of(' ') + 1);
    }
}

double QRenderUtility::smooth(int edge0, int edge1, int x)
{
    if (x <= edge0)
        return 0.0;
    else if (x >= edge1)
        return 1.0;
    else
        return (double)(x - edge0) / (edge1 - edge0);
}

unsigned int QRenderUtility::getSize(unsigned int target)
{
    switch (target)
    {
    case GL_UNSIGNED_BYTE:
        return 1;
    case GL_UNSIGNED_SHORT:
        return 2;
    case GL_FLOAT:
        return 4;
    case GL_DOUBLE:
        return 8;
    default:
        return 0;
    }
}

#if defined WIN32
#include <windows.h>

unsigned char QRenderUtility::getTime(double &time)
{
    LARGE_INTEGER performanceFrequency, performanceCounter;

    if (!QueryPerformanceFrequency(&performanceFrequency))
    {
        std::cerr << " > ERROR: query performance frequency failed." << std::endl;
        return GL_FALSE;
    }

    QueryPerformanceCounter(&performanceCounter);
    time = (performanceCounter.QuadPart * 1000.0) / performanceFrequency.QuadPart;
    return GL_TRUE;
}
#else
#include <sys/time.h>

unsigned char QRenderUtility::getTime(double *time)
{
    struct timeval 1.0;
    gettimeofday(&1.0, NULL);
    time = (1.0.tv_sec * 1e6 + 1.0.tv_usec) / 1000.0;
    return GL_TRUE;
}
#endif
